/*
 * HTML form DOM generator
 * By Tunde Ashafa <http://ashafa.com/>
 * Copyright 2008 Tunde Ashafa, under the MIT License
 */
var HTMLForm = {};


HTMLForm.generator = function(req){
	
	var formValues = {};
	var formData = {};
	
	req && req.paramNames.forEach(function(i){
		if(!/_$/.test(i)){
			formData[i] = req.param(i);
		}
		formValues[i] = req.param(i);
	});
	
	
	
	// creates the html of a form using the properties of the args object
	// and also validates on creation 
	this.create = function(args){
		
		var formId = args.id || "form";
		
		var action = args.action || "";
		var method = (args.method && args.method.toLowerCase() == "post") ? args.method : "get";
		var enctype = args.enctype || "application/x-www-form-urlencoded";
		var inputs = args.inputs || [];
		
		var customErrors = {};
		
		var formObject =  {
			id : formId,
			
			pass: !errorExists(inputs) &&  formValues["htmlform_generator_form_id_"] == formId,
			
			values: formValues,
			
			data: formData,
			
			html: function(showErrors){ 
				showErrors = showErrors && (formValues["htmlform_generator_form_id_"] == formId);
				return "<form id='" + formId + "' action='" + action + "' method='" + method + "'>" 
				+ createFormLabelsAndInputs(inputs, formId, showErrors, customErrors) + 
				"<input name='htmlform_generator_form_id_' type='hidden' value='" + formId + "'>" +
				"</form>";
			},
			
			addError: function(input, errorMsg){
				if(!customErrors[input]){
					customErrors[input] = {}
				}
				if(!customErrors[input][input]){
					customErrors[input][input] = [];
				}
				customErrors[input][input].push({custom: errorMsg});
				this.pass = false;
			},
			
			toString: function(){
				return "[HTMLForm Object #"+formId+"]";	
			}
		}
		
		return formObject;
	}
	
	this.validate = function(rules){
		
		var errors;
			
		$.each(rules,function(input, rule){
			errors = processValidationRule(input, rule, errors);
		});
		
		if(errors.toSource().length > 4){
			return errors;
		}
	}
	
	var errorExists = function(inputs){
		var containsError = false;
		for(var input in inputs){
			$.each(inputs[input], function(itemName, rule){
				if(rule.validate && processValidationRule(itemName, rule.validate)[itemName]){
					containsError = true;
					return false;
				}
			});
			if(containsError) break;
		}
		return containsError;
	}
	
		
	var createFormLabelsAndInputs = function (inputs, formId, showErrors, customErrors){
		var buffer = [];
		
		for(var input in inputs){
			$.each(inputs[input], function(itemName, rule){
				var errors = customErrors[itemName] || null;
				if(!errors && rule.validate){
					errors = processValidationRule(itemName, rule.validate);
				}
				buffer[buffer.length] = createInputAndLabel(itemName, formId, (rule.value != undefined) ? rule.value.toString() : formValues[itemName], rule, (showErrors) ? errors : null);
			});
		}
		
		return  buffer.join("");
	}
	
	
	var createInputAndLabel = function(itemName, formId, value ,itemRule, errors){
		return (itemRule) ? createLabel(itemName, formId, itemRule) + createInput(itemName, formId, value, itemRule) + createErrors(errors, itemName) + "<br>": "";
	}
	
	function createLabel(itemName, formId, itemRule){
		var forAttribute = (itemRule.type == "checkbox") ? "" :  " for='"+formId+"_"+itemName+"'";
		return (itemRule && itemRule.label) ? "<label "+forAttribute+">" + itemRule.label + "</label>" : "";
	}
	
	var createInput = function(itemName, formId, value ,itemRule){
		
		var type = "";
		var str = "";
		var choices = null;
		var itemId = formId + "_" + itemName;
					
		if(itemRule){
			type = (itemRule.type) ? itemRule.type.toLowerCase() : (itemRule.choices) ? "select" : "";
			value = (itemRule.value) ? itemRule.value : value;
			choices = (itemRule.choices) ? itemRule.choices : null;
		}
		
		if((type === "select" || type === "checkbox" || type === "submits") && typeof(choices) !== "object"){
			var opts = {};
			opts[choices] = choices;
			choices = [opts];
		}
		// a function to help us change a choice which is justa string to an object with 
		// the same name and value pairs
		var choiceToObject = function(choice){
			if(typeof(choice) === "string"){
				var choices_to_object = {};
				choices_to_object[choice] = choice;
				return choices_to_object;
			}
			return choice;
		}
		
		switch(type){
			case "textarea":
				var rows;
				var cols;	
				value = value || "";
				if(itemRule){
					rows = (itemRule.rows) ? itemRule.rows : 6;
					cols = (itemRule.cols) ? itemRule.cols : 38;
				}	
				str += "<textarea id='"+itemId+"' name='"+itemName+"' rows='"+rows+"' cols='"+cols+"'>"+value+"</textarea>";
				break;
			case "select":
				str += "<select id='"+itemId+"' name='"+itemName+"'>";
				for(var i in choices){
					$.each(choiceToObject(choices[i]),function(j, val){
						var selected = (value != null && (value == j || (typeof(value) != "string" && value[i] == j))) ? " selected='selected'" : "";
						str += "<option value='"+j+"'"+ selected + ">"+val+"&nbsp;&nbsp;&nbsp;</option>";
					});
				};	
				str += "</select>";
				break;
			case "checkbox":
				for(var i in choices){
					$.each(choiceToObject(choices[i]),function(j, val){
						var checked = (value != null && (value == j || (typeof(value) != "string" && value.indexOf(j) > -1))) ? " checked='checked'" : "";
						var id = (choices.length > 1) ? itemId+"_"+i : itemId;
						str += "<input id='"+id+"' name='"+itemName+"' type='checkbox' class='checkbox' value='"+j+"'"+ checked + "/> <label for='"+id+"' class='checkbox'>"+val+"</label>";
					});
				}
				break;
			
			case "password":
				value = (value) ? "value='"+value+"'/>": "/>";
				str += "<input id='"+itemId+"' name='"+itemName+"' type='password' "+value;
				break;
			case "hidden":
				value = (value) ? "value='"+value+"'/>": "/>";
				str += "<input id='"+itemId+"' name='"+itemName+"' type='hidden' "+value;
				break;
			case "submit":
				value = (value) ? "value='"+value+"'/>": "/>";
				str += "<input id='"+itemId+"' name='"+itemName+"' class='button' type='submit' "+value;
				break;
			case "submits":
				for(var i in choices){
					$.each(choiceToObject(choices[i]),function(j, val){
						var id = (choices.length > 1) ? itemId+"_"+i : itemId;
						str += "<input id='"+id+"' name='"+itemName+"'  class='button' type='submit' value='"+val+"'/>";
					});
				};
				break;			
			default:
				value = (value) ? "value='"+value+"'/>": "/>";
				str += "<input id='"+itemId+"' type='text' name='"+itemName+"' "+value;
				break;
		}
		
		if(itemRule.hash)
			str += "<input name='hash' type='hidden' value='"+itemName+"'/>";
		
		return str;
		
	}
	
	var createErrors = function(errors, itemName){
		var errorString = "";
		errorTests = ["required", "maxlength", "minlength", "test", "url", "custom"];
		
		if(errors && errors[itemName]){
			errorTests.forEach(function(test){
				errors[itemName].forEach(function(error){
					if(error[test] && !errorString){
						errorString += "<span class='error'>";
						errorString +=  error[test];
						errorString += "</span>";
					}
				});
			});
		}
		return errorString;
	}
	
	var processValidationRule = function(inputName, rules, errors){
		
		errors = errors || {};
		$.each(rules, function(i, val){
			if(validateInputValue()[i]){
				var customMsg = null;
				if(val && val.constructor == Array && val.length > 0){
					if(val.length > 1) customMsg = val[1]; 
					val = val[0];
				}
				
				var error = validateInputValue(formValues[inputName], val, rules.required, rules.label || "'"+inputName+"'", customMsg)[i]();
					
				if(error){
					if(!errors[inputName]){
						errors[inputName] = [];
					}
					errors[inputName].unshift(error);
					
				}		
			}
		});
		return errors;
	}
	
	var validateInputValue = function(value , validationRule, isRequired, label, customMsg){
		
		var check = (value && value.length > 0) || isRequired;
		
		this.required = function(){
			return (validationRule) ? (value) ? null : {required: ((customMsg) ? customMsg : "{0} is required.").replace(/{0}/g, label || "")}: null;
		}
		
		this.minlength = function(){
			return (check) ? (value && value.length >= validationRule) ? null : {minlength: ((customMsg) ? customMsg : "{0} can not be less than {1} characters.").replace(/{0}/g, label || "").replace(/{1}/g, validationRule)}: null;
		}
		
		this.maxlength = function(){
			return (check) ? (value && value.length <= validationRule) ? null: {maxlength : ((customMsg) ? customMsg : "{0} can not be more than {1} characters.").replace(/{0}/g, label || "").replace(/{1}/g, validationRule)} : null;
		}
		
		this.test = function(){
			return (validationRule(value, formValues)) ? null : {test: ((customMsg) ? customMsg : "{0} did not pass test.").replace(/{0}/g, label || "").replace(/{1}/g, value) };
		}
		
		this.url = function(){
			var regex = new RegExp("^(https?://)"
		        + "?(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?" 
		        + "(([0-9]{1,3}\\.){3}[0-9]{1,3}" 
		        + "|" 
		        + "([0-9a-zA-Z_!~*'()-]+\\.)*" 
		        + "([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z]\\."
		        + "[a-zA-Z]{2,6})" 
		        + "(:[0-9]{1,4})?" 
		        + "((/?)|"
		        + "(/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+/?)$");
			return (check) ? (regex.test(value)) ? null : {url :((customMsg) ? customMsg : "{0} does not match a url.").replace(/{0}/g, label || "")}: null;
		}
		
		return this;
		
	}
	
	
}